// This code is released under the BSD license.
namespace PugLib
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Security.Cryptography;
    using System.Text;
    using System.Text.RegularExpressions;
    using Extensions.Collections;

    /// <summary>
    /// Utility methods useful for implementing identicon functionality.
    /// Version 1.3
    /// </summary>
    /// <author>Jeff Atwood http://www.codinghorror.com/, Jon Galloway http://weblogs.asp.net/jgalloway/</author>
    /// <remarks>
    /// Based on Don Park's Identicons 1.2 Java Code
    /// http://www.docuverse.com/blog/donpark/2007/01/19/identicon-updated-and-source-released
    /// </remarks>
    public static class Identicons
    {
        /// <summary>Sets or returns current IP address mask. Default is 0xffffffff.</summary>
        public const int Mask = unchecked((int)0xffffffff);

        /// <summary>Sets or returns current salt string value.</summary>
        public static String Salt { get; set; }

        [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")]
        private static byte[] GetAddressBytes(string ipaddress)
        {
            byte[] b = new byte[4] { 0, 0, 0, 0 };
            if (!String.IsNullOrEmpty(ipaddress))
            {
                string s = Regex.Match(ipaddress, @"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}").ToString();
                int i = 0;
                MatchCollection matches = Regex.Matches(s, @"\d+");
                matches.ForEach<Match>(m =>
                                           {
                                               Byte.TryParse(m.ToString(), out b[i]);
                                               i++;
                                           });
            }
            return b;
        }

        /// <summary>
        /// Returns Identicon code for given IP address as an integer.
        /// </summary>
        public static int Code(string ipaddress)
        {
            if (Salt == null)
            {
                // if not set manually, salt is automatically set to some machine-specific stuff
                Salt = Environment.ProcessorCount + Environment.MachineName;
            }

            byte[] ip = GetAddressBytes(ipaddress);
            StringBuilder s = new StringBuilder(32);
            /// Current implementation uses first four bytes of SHA1(int(mask(ip))+salt)
            /// where mask(ip) uses inetMask to remove unwanted bits from IP address.
            s.Append((((ip[0] & 0xFF) << 24) | ((ip[1] & 0xFF) << 16) | ((ip[2] & 0xFF) << 8) | (ip[3] & 0xFF)) & Mask);
            s.Append('+');
            /// Also, since salt is a string for convenience sake, int(mask(ip)) is
            /// converetd into a string and combined with inetSalt prior to hashing.
            s.Append(Salt);
            byte[] hashedIp;

            using (SHA1 md = new SHA1CryptoServiceProvider())
            {
                hashedIp = md.ComputeHash(new UTF8Encoding().GetBytes(s.ToString()));
            }
            return ((hashedIp[0] & 0xFF) << 24) | ((hashedIp[1] & 0xFF) << 16) | ((hashedIp[2] & 0xFF) << 8) | (hashedIp[3] & 0xFF);
        }

        /// <summary>
        /// returns unique string tag for an Identicon code at a specific size. 
        /// Used to track browser caching of specific images
        /// </summary>		
        public static String ETag(int code, int size)
        {
            return "W/\"" + Convert.ToString(code, 16) + "@" + size + "\"";
        }
    }
}